package com.wang.helloworld.hellojetty;

import java.io.IOException;
import java.io.InputStream;
import java.util.EnumSet;
import java.util.Properties;

import javax.servlet.DispatcherType;
import javax.servlet.Servlet;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.DispatcherServlet;

/**
 * Hello world!
 *
 */
public class JettyStarter 
{
	private static final String CONFIG_LOCATION = "com.wang.helloworld.hellojetty.config";
    private static final String DEFAULT_MAPPING_URL = "/*";
    private static final String DEFAULT_CONTEXT_PATH = "/";
    private static final String DEFAULT_SERVICE_PORT = "80";
    private static final String PROPERTIES_FILE = "/jetty.properties";

    private Properties prop = new Properties();
    
    public static void main(String[] args)
    {
    	try{
	    	JettyStarter jettyStarter = new JettyStarter();
	    	jettyStarter.startJetty();
    	}
    	catch(Exception e)
    	{
    		e.printStackTrace();
    	}
    }
    
    private void startJetty() throws Exception
    {
		InputStream input = JettyStarter.class.getResourceAsStream(PROPERTIES_FILE); 
        prop.load(input);
        input.close();

    	Server server = new Server(Integer.valueOf(prop.getProperty("http.jetty.port", DEFAULT_SERVICE_PORT)));
    	
    	/**
    	 *  In this example, we are supposing Jetty will load two web applicaitons, one is Sevlet based, the other one is JSP based.
    	 */
    	HandlerCollection handlers = new HandlerCollection();

    	handlers.addHandler(getResourceHandler());
    	handlers.addHandler(getServletContextHandler());
    	// handlers.addHandler(getWebAppContext());
    	
        server.setHandler(handlers);
        server.start();
        server.join();
    }

    /**
     * ResourceHandler loads html, image as static resource and exposes to browser directly.
     */
    private ResourceHandler getResourceHandler() throws IOException
    {
    	ResourceHandler resourceHandler = new ResourceHandler();
    	resourceHandler.setDirectoriesListed(false);
    	resourceHandler.setResourceBase(new ClassPathResource("webapp/static").getURI().toString());
    	return resourceHandler;
    }

	/**
	 *  WebAppContext actually support external war application.
	 *  With this class, wa can add an other web application to the server.
	 */
    private WebAppContext getWebAppContext() throws IOException
    {
    	WebAppContext webApp = new WebAppContext();
        webApp.setContextPath(DEFAULT_CONTEXT_PATH);
        webApp.setResourceBase(new ClassPathResource("webapp").getURI().toString());
        return webApp;
    }

    private ServletContextHandler getServletContextHandler() throws IOException {

    	WebApplicationContext context = getContext();

    	ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
        
        /**
         * Default Jetty ErrorHandler is removed, we want errors to be handled by DispatcherServlet.
         *  
         * DispatcherServlet dispatches http request to servlets(controllers, JspServlet, DefaultServlet...).
         * Controllers are loaded by a @Configuration bean with @EnableWebMvc annotation, such like "WebMvcConfig.class" in this example
         */
        contextHandler.setErrorHandler(null);
        DispatcherServlet dispatcherServlet = new DispatcherServlet(context);

        ServletHolder servletHolder = new ServletHolder("Servlet Dispatcher", dispatcherServlet);
        servletHolder.setInitOrder(1);
        contextHandler.addServlet(
        		servletHolder, 
        		prop.getProperty("http.jetty.mapping_path", DEFAULT_MAPPING_URL));

        /**
         * ContextLoaderListener loads back-end Spring bean from @Configuration bean, such like "ApplicationConfig.class" in this example
         */
        contextHandler.addEventListener(new ContextLoaderListener(context));
        
        /**
         * setResourceBase() sets the base path for web application resources to the /webapp directory on the classpath. 
         * This will be used to serve resources such as JavaScript files, stylesheets or images, just like in ordinary WAR file.
         * 
         * Another thing to take care of is to include contents of src/main/webapp directory in the JAR on "/webapp" location. 
         * This needs to be added to the <build> section of pom.xml:
			<resources>
			    <resource>
			        <targetPath>/webapp</targetPath>
			        <directory>src/main/webapp</directory>
			    </resource>
			    <resource>
			        <directory>src/main/resources</directory>
			    </resource>
			</resources>
         */
        contextHandler.setResourceBase(new ClassPathResource("webapp/WEB-INF").getURI().toString());
        
        contextHandler.setContextPath(prop.getProperty("http.jetty.context_path", DEFAULT_CONTEXT_PATH));
        
        /**
         * Add Spring Security Filter
         */
        contextHandler.addFilter(new FilterHolder(new DelegatingFilterProxy("springSecurityFilterChain")), "/*", EnumSet.allOf(DispatcherType.class));

        return contextHandler;
    }

    private WebApplicationContext getContext() throws IOException {
    	/**
    	 * Spring's AnnotationConfigWebApplicationContext is created, with the following settings:
    	 *  . setConfigLocation() points to package where @Configuration classes are located.
    	 *  . Default profile is set to "dev", this can be overriden by setting spring.profiles.active environment variable.
    	 */
    	AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation(CONFIG_LOCATION);
        context.getEnvironment().setActiveProfiles(prop.getProperty("spring.profiles.active", "dev"));

        return context;
    }
}
